/*
 * @(#)ResourceLoader.java $version 2012-3-2
 * 
 * Copyright 2011 RogueDevelopment Gruop. All rights Reserved.
 * ROGUEDEVELOPMENT PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nadostar.extensions.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author LEESUNG
 * 
 */
public class ResourceLoader {
	private final Log log = LogFactory.getLog(ResourceLoader.class);

	/**
	 * internal Class Loader
	 */
	private ClassLoader classLoader;

	/**
	 * 우선적으로 콘텍스트 클래스 로더를 찾으며 이를 지정하지 않은 경우 {@link ResourceLoader}의 defining
	 * class loader를 사용한다.
	 */
	public ResourceLoader() {
		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();

		if (classLoader == null) {
			classLoader = this.getClass().getClassLoader();
		}
		if (classLoader == null) {
			classLoader = ClassLoader.getSystemClassLoader();
		}

		this.classLoader = classLoader;
	}

	/**
	 * 사용자가 전달한 클래스 로더를 사용한다.
	 * 
	 * @param classLoader
	 *            사용자 지정 {@link ClassLoader}
	 */
	public ResourceLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	/**
	 * 현재 {@link ResourceLoader}가 사용하는 클래스 로더를 반환한다.
	 * 
	 * @return {@link ClassLoader}
	 */
	public ClassLoader getClassLoader() {
		return classLoader;
	}

	/**
	 * 클래스 패스에 있는 자원을 URL Type으로 반환한다.
	 * 
	 * @param resourceName
	 *            찾고자 하는 자원명
	 * @return URL
	 */
	public URL getResource(String resourceName) {
		return classLoader.getResource(resourceName);
	}

	/**
	 * 같은 이름에 해당하는 {@link URL} {@link Enumeration}을 반환한다. 이름에 해당하는 리소스가 없는 경우에도
	 * 비어있는 {@link Enumeration}을 반환한다.
	 * 
	 * @param resourceName
	 *            String
	 * @return Enumeration<URL>
	 */
	public Enumeration<URL> getUrls(String resourceName) {
		try {
			return classLoader.getResources(resourceName);
		} catch (IOException e) {
			log.warn(resourceName + " does not exist");
		}
		return Collections.enumeration(Collections.<URL> emptyList());
	}

	/**
	 * 클래스 패스에 있는 자원을 InputStream Type으로 반환한다.
	 * 
	 * @param resourceName
	 *            찾고자 하는 자원명
	 * @return InputStream
	 */
	public InputStream getResourceAsStream(String resourceName) {
		return classLoader.getResourceAsStream(resourceName);
	}

	/**
	 * 클래스 패스에 있는 자원을 File Type으로 반환한다.
	 * 
	 * @param resourceName
	 *            찾고자 하는 자원명
	 * @return File
	 */
	public File getFile(String resourceName) {
		String file = getFilename(resourceName);

		if (file == null) {
			return null;
		}

		return new File(file);
	}

	/**
	 * 클래스 패스에 있는 자원의 filename을 반환한다.
	 * 
	 * @param resourceName
	 *            찾고자 하는 자원명
	 * @return String
	 */
	public String getFilename(String resourceName) {
		URL url = getResource(resourceName);

		if (url == null) {
			log.warn(resourceName + " does not exist");
			return null;
		}

		return url.getFile();
	}

	/**
	 * 클래스 패스 내에 자원이 존재하는지 여부를 반환한다.
	 * 
	 * @param resourceName
	 *            찾고자 하는 자원명
	 * @return boolean
	 */
	public boolean fileExists(String resourceName) {
		return getResource(resourceName) != null;
	}

	/**
	 * 클래스 패스내의 자원을 찾아서 {@link Resource} 형태로 반환한다.
	 * 
	 * @param resourceName
	 *            String
	 * @return Resource
	 */
	public Resource loadResource(String resourceName) {
		File rootPath = new File(resourceName);

		if (rootPath.exists()) {
			return new FileResource(rootPath);
		}

		URL resourceURL = classLoader.getResource(resourceName);

		if (resourceURL == null) {
			return null;
		}
		if (resourceURL.getProtocol().equals("file")) {
			File urlPath = new File(resourceURL.getFile());
			return new FileResource(urlPath);
		}

		return new SingleURLResource(resourceName, resourceURL);
	}

	/**
	 * 클래스 패스내의 모든 자원을 찾아서 List 형태로 반환한다.
	 * 
	 * @param resourceName
	 *            String
	 * @return List<Resource>
	 */
	public List<Resource> loadResources(String resourceName) {
		List<Resource> result = new ArrayList<Resource>();
		try {
			Enumeration<URL> enums = classLoader.getResources(resourceName);

			while (enums.hasMoreElements()) {
				URL resourceURL = enums.nextElement();

				if (resourceURL.getProtocol().equals("file")) {
					File urlPath = new File(resourceURL.getFile());
					result.add(new FileResource(urlPath));
				} else {
					result.add(new SingleURLResource(resourceName, resourceURL));
				}
			}

			return result;
		} catch (IOException e) {
			return Collections.emptyList();
		}
	}
}
